﻿/*
SINACOR - BMF&Bovespa
Arquivo: CalculoFinanceiro.cs
Criado em: 23-jul-2010 12:11:10
Arquiteto Responsável: p-wgeneroso
*/
using System;
using System.Collections.Generic;
using System.Text;
using Sinacor.Infra.Core.ProcessManager.Server;
using Sinacor.Infra.Common.Exceptions;
using Sinacor.Infra.Service.ExceptionHandling;
using Sinacor.Infra.Common.Validation;
using PSE.ProcessManager.Common.Process;
using Sinacor.Infra.Service.Security.Authorization;
using PSE.ProcessManager.Server.Process.Security;
using Sinacor.Infra.Core.ProcessManager.Server.Security;
using System.Linq;

namespace Sinacor.Process.Bovespa.CalculoFinanceiro.Server.ProcessLogic 
{
	public class CalculoFinanceiro : BaseProcessLogic 
	{
		#region Atributos

        private int _erroMovimentoFechado = 109841;
        private int _erroFinanceiroCalculado = 109842;
		private System.Guid _idProcesso = System.Guid.NewGuid();
        private int _erroNaoEsperado = 109843;

		#endregion

		#region Eventos

		public event EventHandler ProcessamentoIniciado;

		#endregion

		#region Propriedades

		public int ErroMovimentoFechado
		{
			get { return _erroMovimentoFechado; }
			set { _erroMovimentoFechado = value; }
		}

		public int ErroFinanceiroCalculado
		{
			get { return _erroFinanceiroCalculado; }
			set { _erroFinanceiroCalculado = value; }
		}

		public int ErroNaoEsperado
		{
			get { return _erroNaoEsperado; }
			set { _erroNaoEsperado = value; }
		}

		#endregion

		#region Métodos Públicos

		/// 
		/// <param name="entity"></param>
		public List<ProcessEntity.CalculoFinanceiroResultado> CalcularFinanceiros(ProcessEntity.Parameter.FiltroCalculoFinanceiro entity)
		{
            Communication.ControleMovimentoBolsa communicatonControleMovimentoBolsa = null;
            Communication.ControleMovimentoOrdens communicatonControleMovimentoOrdens = null;
            Communication.FinanceiroPendente communicatonFinanceiroPendente = null;
            ProcessEntity.InformacoesMovimento informacaoMovimento = null;
            List<ProcessEntity.CalculoFinanceiroResultado> calculosFinanceiros = null;
            int[] codigoBrokers = null;
            decimal porcentagem = 0;
            decimal porcentagemItem;
            int atualizarStatus;

            try
            {
                AtualizaStatus(porcentagem, "Bovespa.ResumoFinanceiro.Label.ProcessamentoIniciado");

                if (ProcessamentoIniciado != null)
                {
                    ProcessamentoIniciado(this, new EventArgs());
                }

                if (!IsAborted)
                {
                    if (entity.TipoSistema == ProcessEntity.Enum.TipoSistema.Bolsa)
                    {
                        communicatonControleMovimentoBolsa = new Communication.ControleMovimentoBolsa();
                        informacaoMovimento = communicatonControleMovimentoBolsa.ObterInformacoesMovimento();
                    }
                    else
                    {
                        communicatonControleMovimentoOrdens = new Communication.ControleMovimentoOrdens();
                        informacaoMovimento = communicatonControleMovimentoOrdens.ObterInformacoesMovimento();
                    }

                    if (informacaoMovimento.EstadoMovimento != ProcessEntity.Enum.TipoMovimento.Aberto)
                    {
                        throw new BusinessException(ErroMovimentoFechado);
                    }

                    porcentagem = 10;
                    AtualizaStatus(porcentagem, "Bovespa.ResumoFinanceiro.Label.ConsultandoFinanceirosPendentes");

                    if (!IsAborted)
                    {
                        if (entity.CalculaTudo)
                        {
                            communicatonFinanceiroPendente = new Communication.FinanceiroPendente();
                            entity.FinanceirosPendentes = communicatonFinanceiroPendente.ConsultarFinanceirosPendentes(entity.Filtro);
                        }

                        calculosFinanceiros = MarcarClientesEmProcessamento(entity.FinanceirosPendentes);

                        porcentagem = 50;
                        AtualizaStatus(porcentagem, "Bovespa.ResumoFinanceiro.Label.CalculandoFinanceiro");

                        if (!IsAborted)
                        {
                            codigoBrokers = ConsultarBrokeres(calculosFinanceiros);
                            porcentagemItem = (codigoBrokers.Length > 0) ? 50M / Convert.ToDecimal(codigoBrokers.Length) : 50;
                            atualizarStatus = (codigoBrokers.Length < 100) ? 5 : 100;


                            for (int i = 0; i < codigoBrokers.Length; i++)
                            {
                                if (IsAborted)
                                    break;

                                porcentagem += porcentagemItem;
                                CalcularFinanceiroClientes(ref calculosFinanceiros, codigoBrokers[i], entity.MaquinaUsuario);

                                if (i % atualizarStatus == 0)
                                {
                                    AtualizaStatus(porcentagem, "Bovespa.ResumoFinanceiro.Label.CalculandoFinanceiro");
                                }
                            }
                        }
                    }
                }

                 if (!IsAborted)
                 {
                     SendStatus(100, "Bovespa.ResumoFinanceiro.Label.CalculoFinanceiroConcluído");
                 }
            }
            catch
            {
                RollbackTransaction();

                LimparClientesEmProcessamento();

                throw;
            }

            return calculosFinanceiros;
		}

        #endregion

        #region Métodos Privados

		/// 
		/// <param name="entity"></param>
		private int[] ConsultarBrokeres(List<ProcessEntity.CalculoFinanceiroResultado> entity)
		{
            List<int> retorno = new List<int>();
            foreach (ProcessEntity.CalculoFinanceiroResultado item in entity)
            {
                if (!retorno.Contains(item.FinanceiroPendente.CodigoBroker))
                {
                    retorno.Add(item.FinanceiroPendente.CodigoBroker);
                }
            }

            return retorno.ToArray();
		}

		/// 
		/// <param name="entity"></param>
		private List<ProcessEntity.CalculoFinanceiroResultado> MarcarClientesEmProcessamento(List<ProcessEntity.FinanceiroPendente> entity)
		{
            List<ProcessEntity.FinanceiroPendente> consulta;
            ProcessEntity.FinanceiroPendente item;
            ProcessEntity.CalculoFinanceiroResultado itemAux;
            List<ProcessEntity.CalculoFinanceiroResultado> calculosFinanceiros = new List<ProcessEntity.CalculoFinanceiroResultado>();
            Communication.FinanceiroPendente communicationFinanceiroPendente = new Communication.FinanceiroPendente();
            ResourceAccess.CalculoFinanceiro raCalculoFinanceiro = new ResourceAccess.CalculoFinanceiro();
            decimal porcentagem = 20;
            decimal porcentagemItem;
            int atualizarStatus;
            string resourceKeyOut;
            string errorMessage;

            AtualizaStatus(porcentagem, "Bovespa.ResumoFinanceiro.Label.PreparandoCalculosFinanceiros");

            porcentagemItem = (entity.Count > 0) ? 30M / Convert.ToDecimal(entity.Count) : 30;
            atualizarStatus = (entity.Count < 100) ? 5 : 100;

            for (int i = 0; i < entity.Count; i++)
            {
                item = entity[i];

                consulta = communicationFinanceiroPendente.ConsultarFinanceirosPendentes(new ProcessEntity.Parameter.FiltroFinanceiroPendente
                                                                                                    {
                                                                                                        DataPregao = item.DataPregao,
                                                                                                        IndicadorBroker = item.IndicadorBroker,
                                                                                                        CodigoCliente = item.CodigoCliente
                                                                                                    });

                itemAux = new ProcessEntity.CalculoFinanceiroResultado();

                itemAux.FinanceiroPendente = item;

                if (consulta == null
                    || consulta.Count == 0)
                {
                    itemAux.Sucesso = true;

                    errorMessage = ErrorMessage.RetrieveErrorMessage(ErroFinanceiroCalculado, out resourceKeyOut);
                    itemAux.Erro = new ValidationError(ErroFinanceiroCalculado, errorMessage, resourceKeyOut);
                }
                else
                {
                    raCalculoFinanceiro.IncluirClienteEmProcessamento(item, _idProcesso);
                    itemAux.Sucesso = true;
                    itemAux.Erro = null;
                }

                calculosFinanceiros.Add(itemAux);

                porcentagem += porcentagemItem;

                if (i % atualizarStatus == 0)
                {
                    AtualizaStatus(porcentagem, "Bovespa.ResumoFinanceiro.Label.PreparandoCalculosFinanceiros");
                }
            }

            return calculosFinanceiros;
		}

		/// 
		/// <param name="entity"></param>
		private bool VerificarFinanceiroCalculado(ProcessEntity.FinanceiroPendente entity)
		{
            bool retorno = false;

            Communication.FinanceiroPendente communicationFinanceiroPendente = new Communication.FinanceiroPendente();
            List<ProcessEntity.FinanceiroPendente> consulta = communicationFinanceiroPendente.ConsultarFinanceirosPendentes(new ProcessEntity.Parameter.FiltroFinanceiroPendente
                                                                                        {
                                                                                           DataPregao = entity.DataPregao,
                                                                                           CodigoCliente = entity.CodigoCliente,
                                                                                           IndicadorBroker = ProcessEntity.Enum.TipoCliente.Cliente
                                                                                        });

            retorno = (consulta == null
                       || consulta.Count == 0);

            return retorno;
		}

		/// 
		/// <param name="entity"></param>
		/// <param name="codigoBroker"></param>
        private void CalcularFinanceiroClientes(ref List<ProcessEntity.CalculoFinanceiroResultado> entity, int codigoBroker, string computadorOrigem)
		{
            string errorMessage;
            string resourceKeyOut;
            ProcessEntity.Resultado resultado;
            ValidationError erroCalculoFinanceiroBroker;
            ResourceAccess.CalculoFinanceiro raCalculoFinanceiro = new ResourceAccess.CalculoFinanceiro();
            List<ProcessEntity.CalculoFinanceiroResultado> processar = entity.FindAll(delegate(ProcessEntity.CalculoFinanceiroResultado item)
                                                                                            {
                                                                                                return (item.FinanceiroPendente.CodigoBroker == codigoBroker);
                                                                                            });

            BeginTransaction(AuthenticationContext<SinacorAuthenticationData>.Current.AuthenticationData.CompanyId);

            foreach (ProcessEntity.CalculoFinanceiroResultado item in processar)
            {
                if (VerificarFinanceiroCalculado(item.FinanceiroPendente))
                {
                    item.Sucesso = true;

                    errorMessage = ErrorMessage.RetrieveErrorMessage(ErroFinanceiroCalculado, out resourceKeyOut);
                    item.Erro = new ValidationError(ErroFinanceiroCalculado, errorMessage, resourceKeyOut);
                }
                else
                {
                    resultado = raCalculoFinanceiro.CalcularFinanceiroCliente(item.FinanceiroPendente, computadorOrigem);

                    if (resultado.Sucesso)
                    {
                        item.Sucesso = true;
                        item.Erro = null;
                    }
                    else
                    {
                        item.Sucesso = false;
                        item.Erro = TratarExcecoes(resultado);
                    }
                }

                raCalculoFinanceiro.ExcluirClienteEmProcessamento(item.FinanceiroPendente);
            }

            if (!CalcularFinanceiroBroker(codigoBroker, ProcessEntity.Enum.TipoCliente.Broker, out erroCalculoFinanceiroBroker, computadorOrigem))
            {
                foreach (ProcessEntity.CalculoFinanceiroResultado item in processar)
                {
                    if (item.Sucesso)
                    {
                        item.Sucesso = false;
                        item.Erro = erroCalculoFinanceiroBroker;
                    }
                }
            }

            if (!CalcularFinanceiroBroker(codigoBroker, ProcessEntity.Enum.TipoCliente.Financeiro, out erroCalculoFinanceiroBroker, computadorOrigem))
            {
                foreach (ProcessEntity.CalculoFinanceiroResultado item in processar)
                {
                    if (item.Sucesso)
                    {
                        item.Sucesso = false;
                        item.Erro = erroCalculoFinanceiroBroker;
                    }
                }
            }

            if (processar.Count<ProcessEntity.CalculoFinanceiroResultado>(delegate(ProcessEntity.CalculoFinanceiroResultado item)
                                                                                        {
                                                                                            return item.Sucesso;
                                                                                        }
               ) > 0)
            {
                CommitTransaction();
            }
            else
            {
                RollbackTransaction();
            }
		}

		/// 
		/// <param name="codigoBroker"></param>
		/// <param name="tipoBroker"></param>
		/// <param name="erro"></param>
        private bool CalcularFinanceiroBroker(int codigoBroker, ProcessEntity.Enum.TipoCliente tipoBroker, out ValidationError erro, string computadorOrigem)
		{
            bool retorno = true;            
            ProcessEntity.Resultado resultado;
            ResourceAccess.CalculoFinanceiro raCalculoFinanceiro = new ResourceAccess.CalculoFinanceiro();
            List<ProcessEntity.FinanceiroBroker> financeiro = raCalculoFinanceiro.ConsultarFinanceirosBrokeres(codigoBroker, tipoBroker);

            erro = null;

            foreach (ProcessEntity.FinanceiroBroker item in financeiro)
            {
                resultado = raCalculoFinanceiro.ProcessarFinanceiroBroker(item, tipoBroker, computadorOrigem);

                if (!resultado.Sucesso)
                {
                    erro = TratarExcecoes(resultado);
                    retorno = false;
                    break;
                }
            }

            return retorno;
		}

		/// 
		/// <param name="entity"></param>
		private ValidationError TratarExcecoes(ProcessEntity.Resultado entity)
		{
            ValidationError retorno;
            List<Object> _params;
            string resourceKeyOut;
            string errorMessage;

            if (!String.IsNullOrEmpty(entity.Argumentos))
            {
                _params = ErrorMessage.ConvertXmlArgumentsToParameters(entity.Argumentos);

                errorMessage = ErrorMessage.RetrieveErrorMessage(entity.CodigoErro, out resourceKeyOut, _params.ToArray());

                retorno = new ValidationError(entity.CodigoErro, errorMessage, resourceKeyOut, _params);
            }
            else if (entity.CodigoErro > 0)
            {
                errorMessage = ErrorMessage.RetrieveErrorMessage(entity.CodigoErro, out resourceKeyOut);

                retorno = new ValidationError(entity.CodigoErro, errorMessage, resourceKeyOut);
            }
            else
            {
                errorMessage = ErrorMessage.RetrieveErrorMessage(ErroNaoEsperado, out resourceKeyOut);
                retorno = new ValidationError(ErroNaoEsperado, errorMessage, resourceKeyOut);
            }

            return retorno;
		}

        private void AtualizaStatus(decimal progress, string resourceKey)
        {
            this.Status.Progress = Convert.ToInt32(Math.Round(progress, 0, MidpointRounding.ToEven));
            this.Status.Step = resourceKey;
            this.SendStatus();
        }

        private void LimparClientesEmProcessamento()
        {
            ResourceAccess.CalculoFinanceiro rsCalculoFinanceiro = new ResourceAccess.CalculoFinanceiro();
            rsCalculoFinanceiro.ExcluirClientesProcesso(_idProcesso);
        }

        #endregion
	}
}